<?php
/**
 * 公共函数-与laravel和系统无关的函数
 * User: dxk
 * Date: 2020/07/17
 */

use JetBrains\PhpStorm\NoReturn;
use JetBrains\PhpStorm\Pure;

/**
 * 获取之前时间距离现在的自然描述
 * 1、1分钟内 ：刚刚
 *
 * 2、24小时内：N分钟前/N小时前
 *
 * 例：5分钟前/13小时前
 *
 * 3、超过24小时：以天/月形式
 *
 * 例：3天前/2个月前
 * @param $datetime
 * @return string
 */
function easy_get_nature_datetime($datetime): string
{
    $diff_time = time() - strtotime($datetime);
    if ($diff_time <= 60) {
        $str = '刚刚';
    } elseif ($diff_time < 3600) {
        $str = sprintf('%d分钟前', floor($diff_time / 60));
    } elseif ($diff_time < 86400) {
        $str = sprintf('%d小时前', floor($diff_time / 3600));
    } elseif ($diff_time < 1728000) {
        $str = sprintf('%d天前', floor($diff_time / 86400));
    } else {
        $diff_month = \Carbon\Carbon::createFromTimestamp(strtotime($datetime))->diffInMonths(\Carbon\Carbon::now());
        $str = sprintf('%d月前', $diff_month);
    }
    return $str;
}

/**
 * 信息返回
 * @param int $code 错误码 0成功，其他失败
 * @param string $msg 返回消息
 * @param array $data 返回数据
 * @return array
 */
function alert_info(int $code = 0, string $msg = '', array $data = []): array
{
    $msg = trim($msg);
    return ['code' => $code, 'msg' => $msg, 'data' => $data];
}

/**
 * 判断一个数组是否只包含有目标元素
 * @param array $target_array
 * @param array $only_has_items
 * @return bool
 */
function easy_only_in_array(array $target_array, array $only_has_items): bool
{
    foreach ($target_array as $item) {
        if (!in_array($item, $only_has_items)) {
            return false;
        }
    }
    return true;
}

/**
 * 分隔的字符串转数组，子项 trim 过滤空字符串，去重
 * @param $int_str
 * @param string $explode
 * @param bool $is_only_gt_zero 是否只获取大于0的数据
 * @return array
 */
function easy_int_explode($int_str, string $explode = ',', bool $is_only_gt_zero = true): array
{
    if (empty($int_str)) {
        return [];
    }
    if (is_array($int_str)) {
        return $int_str;
    }
    $int_str = trim($int_str);
    if ($int_str === '') {
        return [];
    }
    $str = explode($explode, $int_str);
    $arr = [];
    foreach ($str as $s) {
        $s = trim($s);
        if (is_numeric($s)) {
            if (!$is_only_gt_zero || ($s > 0)) {
                $arr[] = (int)$s;
            }
        }
    }
    return array_unique($arr);
}

/**
 * 分隔的字符串转数组，子项 trim 过滤空字符串，去重
 * @param $str
 * @param string $explode
 * @return array
 */
function easy_string_explode($str, string $explode = ','): array
{
    $str = trim($str);
    if ($str === '') {
        return [];
    }
    $str = explode($explode, $str);
    $arr = [];
    foreach ($str as $s) {
        $s = trim($s);
        if ($s !== '') {
            $arr[] = $s;
        }
    }
    return array_unique($arr);
}

function easy_trim($str): array|string//删除空格
{
    $qian = array(" ", "　", "\t", "\n", "\r");
    $hou = array("", "", "", "", "");
    return str_replace($qian, $hou, $str);
}

/**
 * 对象转换为数组
 * @param mixed $obj
 * @return array
 */
function obj_to_array(mixed $obj): array
{
    if (!is_object($obj) && !is_array($obj)) {
        return [];
    }
    return json_decode(json_encode($obj), true);
}

/**
 * xlsx错误处理
 * @param array $item
 * @param int $error_key
 * @param string $msg
 * @return array
 */
function easy_xlsx_error(array $item, int $error_key, string $msg): array
{
    for ($i = 0; $i < $error_key; $i++) {
        if (!isset($item[$i])) {
            $item[$i] = '';
        }
    }
    $item[$error_key] = $msg;
    return $item;
}

/**
 * bootstrap table
 * @param $list
 * @return false|string
 */
function easy_table_filter($list): bool|string
{
    $data_list = [];
    foreach ($list as $k => $v) {
        $data_list[] = ['id' => $k, 'text' => $v];
    }
    return json_encode($data_list, JSON_UNESCAPED_UNICODE);
}

/**
 * 获取数组中的指定字段
 * @param $row
 * @param $field
 * @return array
 */
function easy_get_field($row, $field): array
{
    if (!is_array($row)) {
        return [];
    }
    if (empty($field) || $field == '*' || (is_array($field) && in_array('*', $field))) {
        return $row;
    }
    if (!is_array($field)) {
        $field = [$field];
    }
    foreach ($row as $k => $item) {
        if (!in_array($k, $field)) {
            unset($row[$k]);
        }
    }
    return $row;
}

/**
 * 获取数组中的指定字段
 * @param array $data_list
 * @param array $field
 * @param string $key_field
 * @return array
 */
function easy_array_get_field(array $data_list, array $field = [], string $key_field = ''): array
{
    $list = [];
    foreach ($data_list as $item) {
        if (empty($key_field)) {
            $list[] = easy_get_field($item, $field);
        } else {
            $list[$item[$key_field]] = easy_get_field($item, $field);
        }
    }
    return $list;
}

/**
 * 二维数组排序
 * @param array $data_list
 * @param string $sort_field
 * @param string $sort
 * @return array
 */
function easy_array_sort(array $data_list, string $sort_field, string $sort = 'SORT_DESC'): array
{
    $sort_field_values = [];
    foreach ($data_list as $k => $item) {
        $sort_field_values[$k] = $item[$sort_field];
    }
    array_multisort($sort_field_values, constant($sort), $data_list);
    return $data_list;
}

/**
 * 递归创建目录
 * @param string $dir
 * @param int $mode
 * @return bool
 */
function easy_mk_dir(string $dir, int $mode = 0777): bool
{
    if (is_dir($dir)) {
        return true;
    }
    if (@mkdir($dir, $mode)) {
        @chmod($dir, $mode);
        return true;
    }
    if (!easy_mk_dir(dirname($dir), $mode)) {
        return false;
    }
    $res = @mkdir($dir, $mode);
    if ($res) {
        @chmod($dir, $mode);
    }
    return $res;
}

/**
 * 递归删除目录下所有文件
 * @param string $path
 */
function easy_del_dir(string $path)
{
    $op = dir($path);
    while (false != ($item = $op->read())) {
        if ($item == '.' || $item == '..') {
            continue;
        }
        if (is_dir($op->path . '/' . $item)) {
            easy_del_dir($op->path . '/' . $item);
            rmdir($op->path . '/' . $item);
        } else {
            unlink($op->path . '/' . $item);
        }
    }
}

/**
 * 获取当前时间
 * @return float
 */
function easy_microtime(): float
{
    list($usec, $sec) = explode(' ', microtime());
    return (float)$usec + (float)$sec;
}

/**
 * 获取url的参数
 * @param string $url
 * @return array
 */
function easy_url_params(string $url): array
{
    $url_info = parse_url($url);
    $query = $url_info['query'];
    $queryParts = explode('&', $query);
    $params = [];
    foreach ($queryParts as $param) {
        $item = explode('=', $param);
        $params[$item[0]] = $item[1];
    }
    return $params;
}

/**
 * 字符串转十进制数字，默认为64进制字符串，其他进制可使用base_convert函数
 * @param string $string
 * @param string $pool
 * @return false|float|int
 */
function easy_string_to_num(string $string, string $pool = ''): float|bool|int
{
    if (empty($pool)) {
        $pool = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    }
    $base = strlen($pool);
    $num = 0;
    while (strlen($string) > 0) {
        $pos = strpos($pool, $string[0]);
        if ($pos === false) {
            return false;
        }
        $num += ($pos * pow($base, strlen($string) - 1));
        $string = substr($string, 1);
    }
    return $num;
}

/**
 * 十进制数字转字符串，默认为64进制字符串，其他进制可使用base_convert函数
 * @param int $num
 * @param string $pool
 * @return string
 */
function easy_num_to_string(int $num, string $pool = ''): string
{
    if (empty($pool)) {
        $pool = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
    }
    $base = strlen($pool);
    $code = "";
    while ($num > $base - 1) {
        $k = intval(fmod($num, $base));
        $code = $pool[$k] . $code;
        $num = floor($num / $base);
    }
    return $pool[$num] . $code;
}

/**
 * 产生随机字符串
 * @param int $length 输出长度
 * @param string $chars 可选的 ，默认为 0123456789
 * @return string 字符串
 */
function easy_random(int $length, string $chars = '0123456789'): string
{
    $hash = '';
    $max = strlen($chars) - 1;
    for ($i = 0; $i < $length; $i++) {
        $hash .= $chars[mt_rand(0, $max)];
    }
    return $hash;
}

/**
 * curl_post请求
 * @param string $url
 * @param array|string $post_data
 * @param int $timeout
 * @param array $headers
 * @return bool|string
 * @throws Exception
 */
function easy_curl_post(string $url, array|string $post_data, int $timeout = 20, array $headers = []): bool|string
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_FAILONERROR, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_DNS_CACHE_TIMEOUT, 10);
    if (str_starts_with($url, 'https')) {
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    }
    if (!empty($headers)) {
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    }
    if (is_array($post_data) && 0 < count($post_data)) {
        $postBodyString = "";
        $postMultipart = false;
        foreach ($post_data as $k => $v) {
            if (!str_starts_with($v, "@")) // 判断是不是文件上传
            {
                $postBodyString .= "$k=" . urlencode($v) . "&";
            } else {
                $postMultipart = true;
            }
        }
        unset($k, $v);
        curl_setopt($ch, CURLOPT_POST, true);
        if ($postMultipart) {
            curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
        } else {
            curl_setopt($ch, CURLOPT_POSTFIELDS, substr($postBodyString, 0, -1));
        }
    } elseif (is_string($post_data)) {
        curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
    }
    $reponse = curl_exec($ch);
    if (curl_errno($ch)) {
        throw new Exception(curl_error($ch), 500);
    } else {
        $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        if (200 !== $httpStatusCode) {
            throw new Exception($reponse, $httpStatusCode);
        }
    }
    return $reponse;
}

/**
 * curl_get请求
 * @param string $url
 * @param array $get_data
 * @param int $timeout
 * @param array $headers
 * @return bool|string
 * @throws Exception
 */
function easy_curl_get(string $url, array $get_data = [], int $timeout = 20, array $headers = []): bool|string
{
    if (!empty($get_data)) {
        $url .= '?' . http_build_query($get_data);
    }
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_FAILONERROR, false);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
    curl_setopt($ch, CURLOPT_DNS_CACHE_TIMEOUT, 10);
    if (str_starts_with($url, 'https')) {
        curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
        curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
    }
    if (!empty($headers)) {
        curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    }
    $reponse = curl_exec($ch);
    if (curl_errno($ch)) {
        throw new Exception(curl_error($ch), 500);
    } else {
        $httpStatusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
        if (200 !== $httpStatusCode) {
            throw new Exception($reponse, $httpStatusCode);
        }
    }
    return $reponse;
}

/**
 * app过滤变量
 * @param mixed $var
 * @return array|string
 */
function easy_app_filter_var(mixed $var): array|string
{
    if (is_object($var)) {
        return $var;
    }
    if (is_array($var)) {
        foreach ($var as $k => $item) {
            $var[$k] = easy_app_filter_var($item);
        }
        return $var;
    }
    return trim((string)$var);
}

/**
 * 获取请求ip
 * @return string
 */
function easy_ip(): string
{
    $ip = '';
    if (getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) {
        $ip = getenv('HTTP_CLIENT_IP');
    } elseif (getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) {
        $ip = getenv('HTTP_X_FORWARDED_FOR');
    } elseif (getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) {
        $ip = getenv('REMOTE_ADDR');
    } elseif (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) {
        $ip = $_SERVER['REMOTE_ADDR'];
    }
    return preg_match('/[\d.]{7,15}/', $ip, $matches) ? $matches[0] : '';
}

/**
 * 浮点数比较 1 == 1
 * @param float $f1
 * @param float $f2
 * @param int $precision
 * @return bool
 */
#[Pure] function easy_float_eq(float $f1, float $f2, int $precision = 4): bool
{
    $res = easy_bccomp($f1, $f2, $precision);
    if ($res === 0) {
        return true;
    } else {
        return false;
    }
}

/**
 * 浮点数比较 1 > 0
 * @param float $big
 * @param float $small
 * @param int $precision
 * @return bool
 */
#[Pure] function easy_float_gt(float $big, float $small, int $precision = 4): bool
{
    $res = easy_bccomp($big, $small, $precision);
    if ($res === 1) {
        return true;
    } else {
        return false;
    }
}

/**
 * 浮点数比较 2>=1
 * @param float $big
 * @param float $small
 * @param int $precision
 * @return bool
 */
#[Pure] function easy_float_ge(float $big, float $small, int $precision = 4): bool
{
    $res = easy_bccomp($big, $small, $precision);
    if ($res !== -1) {
        return true;
    } else {
        return false;
    }
}

/**
 * 2个任意精度数字的加法计算
 * @param string $left_operand 左操作数，字符串类型
 * @param string $right_operand 右操作数，字符串类型
 * @param int $scale 此可选参数用于设置结果中小数点后的小数位数
 * @return string
 */
function easy_bcadd(string $left_operand, string $right_operand, int $scale = 14): string
{
    return easy_bc($left_operand, $right_operand, $scale, 'bcadd');
}

/**
 * 2个任意精度数字的减法计算
 * @param string $left_operand 左操作数，字符串类型
 * @param string $right_operand 右操作数，字符串类型
 * @param int $scale 此可选参数用于设置结果中小数点后的小数位数
 * @return string
 */
function easy_bcsub(string $left_operand, string $right_operand, int $scale = 14): string
{
    return easy_bc($left_operand, $right_operand, $scale, 'bcsub');
}

/**
 * 2个任意精度数字乘法计算
 * @param string $left_operand 左操作数，字符串类型
 * @param string $right_operand 右操作数，字符串类型
 * @param int $scale 此可选参数用于设置结果中小数点后的小数位数
 * @return string
 */
function easy_bcmul(string $left_operand, string $right_operand, int $scale = 14): string
{
    return easy_bc($left_operand, $right_operand, $scale, 'bcmul');
}

/**
 * 2个任意精度数字除法计算
 * @param string $left_operand 左操作数，字符串类型
 * @param string $right_operand 右操作数，字符串类型
 * @param int $scale 此可选参数用于设置结果中小数点后的小数位数
 * @return string
 */
function easy_bcdiv(string $left_operand, string $right_operand, int $scale = 14): string
{
    return easy_bc($left_operand, $right_operand, $scale, 'bcdiv');
}

/**
 * 比较两个任意精度的数字
 * 如果两个数相等返回0, 左边的数left_operand比较右边的数right_operand大返回1, 否则返回-1.
 * @param string $left_operand 左操作数，字符串类型
 * @param string $right_operand 右操作数，字符串类型
 * @param int $scale 此可选参数用于设置结果中小数点后的小数位数
 * @return int
 */
function easy_bccomp(string $left_operand, string $right_operand, int $scale = 10): int
{
    $left_operand = number_format((float)$left_operand, $scale, '.', '');
    $right_operand = number_format((float)$right_operand, $scale, '.', '');
    return bccomp($left_operand, $right_operand, $scale);
}

/**
 * bcadd — 2个任意精度数字的加法计算
 * bccomp — 比较两个任意精度的数字
 * bcdiv — 2个任意精度的数字除法计算
 * bcmod — 对一个任意精度数字取模
 * bcmul — 2个任意精度数字乘法计算
 * bcpow — 任意精度数字的乘方
 * bcpowmod — Raise an arbitrary precision number to another, reduced by a specified modulus
 * bcscale — 设置所有bc数学函数的默认小数点保留位数
 * bcsqrt — 任意精度数字的二次方根
 * bcsub — 2个任意精度数字的减法
 * @param string $left_operand 左操作数，字符串类型
 * @param string $right_operand 右操作数，字符串类型
 * @param int $scale 此可选参数用于设置结果中小数点后的小数位数
 * @param string $method
 * @return string
 */
function easy_bc(string $left_operand, string $right_operand, int $scale = 10, string $method = ''): string
{
    $methods = ['bcadd', 'bccomp', 'bcdiv', 'bcmod', 'bcmul', 'bcpow', 'bcpowmod', 'bcscale', 'bcsqrt', 'bcsub'];
    if (in_array($method, $methods)) {
        $left_operand = number_format((float)$left_operand, $scale, '.', '');
        $right_operand = number_format((float)$right_operand, $scale, '.', '');
        return $method($left_operand, $right_operand, $scale);
    } else {
        return '';
    }
}

/**
 * 打印输出过滤
 * @param array|string $subject
 * @return string|array
 */
function easy_trim_print(array|string $subject): string|array
{
    $search = [',', "'", "\r\n", "\n", "\r", "\t", "\\", '"', '(', ')'];
    $replace = ['，', '’', ' ', ' ', ' ', ' ', ' ', ' ', '（', '）'];
    return str_ireplace($search, $replace, $subject);
}

/**
 * csv导出过滤
 * @param array|string $subject
 * @return array|string
 */
function easy_trim_csv(array|string $subject): array|string
{
    $search = ['"', "'", "\r\n", "\n", "\r", ',', "\t"];
    $replace = [' ', '’', ' ', ' ', ' ', '，', ' '];
    return str_ireplace($search, $replace, $subject);
}

/**
 * 检测是否为空的excel单元行
 * @param array $row
 * @return bool
 */
function easy_is_empty_row(array $row): bool
{
    foreach ($row as $item) {
        if (!is_object($item) && !empty(trim($item))) {
            return false;
        }
    }
    return true;
}

/**
 * gbk编码转成utf8编码
 * @param mixed $var
 * @return mixed
 */
function easy_gbk_to_utf8(mixed $var): mixed
{
    if (is_array($var)) {
        foreach ($var as $k => $str) {
            $var[$k] = easy_gbk_to_utf8($str);
        }
    } else {
        $var = mb_convert_encoding($var, 'utf-8', 'gbk');
    }
    return $var;
}

/**
 * utf-8编码转成gbk编码
 * @param mixed $var
 * @return mixed
 */
function easy_utf8_to_gbk(mixed $var): mixed
{
    if (is_array($var)) {
        foreach ($var as $k => $str) {
            $var[$k] = easy_utf8_to_gbk($str);
        }
    } else {
        $var = mb_convert_encoding($var, 'gbk', 'utf-8');
    }
    return $var;
}

/**
 * 获取有效字段
 * @param array $user_fields 用户输入字段
 * @param array $allow_fields 系统允许字段
 * @return array
 */
function easy_get_valid_field(array $user_fields, array $allow_fields): array
{
    $user_fields = array_map('trim', $user_fields);
    //$user_fields = array_map('strtolower', $user_fields); //支持数据表为大写的字段
    foreach ($user_fields as $k => $v) {
        if (empty($v)) {
            unset($user_fields[$k]);
        }
    }
    return array_intersect($user_fields, $allow_fields);
}

/**
 * 生成签名
 * @param array $params
 * @param string $app_secret
 * @return string
 */
function easy_make_sign(array $params, string $app_secret): string
{
    ksort($params);
    $stringToBeSigned = $app_secret;
    foreach ($params as $k => $v) {
        if (!str_starts_with($v, "@")) {
            if (!is_array($v)) {
                $v = stripslashes($v);
            }
            $stringToBeSigned .= "$k$v";
        }
    }
    unset($k, $v);
    $stringToBeSigned .= $app_secret;
    return strtoupper(md5($stringToBeSigned));
}

/**
 * 验证签名
 * @param array $params
 * @param string $app_secret
 * @param string $sign_key
 * @return bool
 */
function easy_check_sign(array $params, string $app_secret, string $sign_key = 'sign'): bool
{
    $sign = trim($params[$sign_key]);
    unset($params[$sign_key]);
    ksort($params);
    $stringToBeSigned = $app_secret;
    foreach ($params as $k => $v) {
        if (!str_starts_with($v, "@")) {
            if (!is_array($v)) {
                $v = stripslashes($v);
            }
            $stringToBeSigned .= "$k$v";
        }
    }
    unset($k, $v);
    $stringToBeSigned .= $app_secret;
    $check_sign = strtoupper(md5($stringToBeSigned));
    if (strcmp($check_sign, $sign) !== 0) {
        return false;
    }
    return true;
}

/**
 * 获取当前连接地址
 * @return string
 */
function easy_get_current_url(): string
{
    if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off' || isset($_SERVER['SERVER_PORT']) && (int)$_SERVER['SERVER_PORT'] === 443) {
        $protocol = 'https://';
    } elseif (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') {
        $protocol = 'https://';
    } else {
        $protocol = 'http://';
    }
    return $protocol . ($_SERVER['HTTP_HOST'] ?? '') . ($_SERVER['REQUEST_URI'] ?? '');
}

/**
 * 打印信息
 * @param mixed ...$vars
 */
#[NoReturn] function easy_p(...$vars)
{
    foreach ($vars as $var) {
        echo '<pre>';
        print_r($var);
        echo '</pre>';
    }
    die;
}

/**
 * 判断是否为手机访问
 * @return bool
 */
function easy_is_mobile(): bool
{
    $regex_match = '/(nokia|iphone|android|motorola|^mot\-|softbank|foma|docomo|kddi|up\.browser|up\.link|';
    $regex_match .= 'htc|dopod|blazer|netfront|helio|hosin|huawei|novarra|CoolPad|webos|techfaith|palmsource|';
    $regex_match .= 'blackberry|alcatel|amoi|ktouch|nexian|samsung|^sam\-|s[cg]h|^lge|ericsson|philips|sagem|wellcom|bunjalloo|maui|';
    $regex_match .= 'symbian|smartphone|midp|wap|phone|windows ce|iemobile|^spice|^bird|^zte\-|longcos|pantech|gionee|^sie\-|portalmmm|';
    $regex_match .= 'jig\s browser|hiptop|^ucweb|^benq|haier|^lct|opera\s*mobi|opera\*mini|320x320|240x320|176x220';
    $regex_match .= ')/i';
    return isset($_SERVER['HTTP_X_WAP_PROFILE']) or isset($_SERVER['HTTP_PROFILE']) or preg_match($regex_match, strtolower($_SERVER['HTTP_USER_AGENT']));
}

/**
 * 判断是否为支付宝
 * @return bool
 */
function easy_is_alipay(): bool
{
    if (str_contains($_SERVER['HTTP_USER_AGENT'], 'AlipayClient')) {
        return true;
    }
    return false;
}

/**
 * 判断是否为微信
 * @param string $user_agent
 * @return bool
 */
function easy_is_wechat(string $user_agent = ''): bool
{
    if (empty($user_agent)) {
        $user_agent = $_SERVER['HTTP_USER_AGENT'];
    }
    if (!str_contains($user_agent, 'MicroMessenger')) {
        return false;
    } else {
        return true;
    }
}

/**
 * 获取微信版本号
 * @return float
 */
function easy_get_wechat_version(): float
{
    $user_agent = $_SERVER['HTTP_USER_AGENT'];
    preg_match('~MicroMessenger/([\d.]+)~', $user_agent, $match);
    return round($match[1], 2);
}

/**
 * 检测是否支付宝刷卡条码 25~30开头的长度为16~24位的数字，实际字符串长度以开发者获取的付款码长度为准
 * @param string $barcode
 * @return bool
 */
function easy_is_alipay_code(string $barcode): bool
{
    $barcode = trim($barcode);
    if (empty($barcode)) {
        return false;
    }
    if (!preg_match('/^(25|26|27|28|29|30)\d{14,22}$/', $barcode)) {
        return false;
    }
    return true;
}

/**
 * 检测是否微信刷卡条码：18位纯数字，以10、11、12、13、14、15开头
 * @param string $barcode
 * @return bool
 */
function easy_is_wechatpay_code(string $barcode): bool
{
    $barcode = trim($barcode);
    if (empty($barcode)) {
        return false;
    }
    if (!preg_match('/^(10|11|12|13|14|15)\d{16}$/', $barcode)) {
        return false;
    }
    return true;
}

/**
 * 获取浏览器以及版本号
 * @param string $user_agent
 * @return array
 */
function easy_get_browser_version(string $user_agent = ''): array
{
    if (empty($user_agent)) {
        $user_agent = $_SERVER['HTTP_USER_AGENT'];
    }
    $browser = '';
    $version = '';
    if (preg_match('/OmniWeb\/(v*)([^\s|;]+)/i', $user_agent, $regs)) {
        $browser = 'OmniWeb';
        $version = $regs[2];
    }
    if (preg_match('/Netscape([\d]*)\/([^\s]+)/i', $user_agent, $regs)) {
        $browser = 'Netscape';
        $version = $regs[2];
    }
    if (preg_match('/safari\/([^\s]+)/i', $user_agent, $regs)) {
        $browser = 'Safari';
        $version = $regs[1];
    }
    if (preg_match('/MSIE\s([^\s|;]+)/i', $user_agent, $regs)) {
        $browser = 'Internet Explorer';
        $version = $regs[1];
    }
    if (preg_match('/Opera[\s|\/]([^\s]+)/i', $user_agent, $regs)) {
        $browser = 'Opera';
        $version = $regs[1];
    }
    if (preg_match('/NetCaptor\s([^\s|;]+)/i', $user_agent, $regs)) {
        $browser = '(Internet Explorer ' . $version . ') NetCaptor';
        $version = $regs[1];
    }
    if (preg_match('/Maxthon/i', $user_agent, $regs)) {
        $browser = '(Internet Explorer ' . $version . ') Maxthon';
        $version = '';
    }
    if (preg_match('/360SE/i', $user_agent, $regs)) {
        $browser = '(Internet Explorer ' . $version . ') 360SE';
        $version = '';
    }
    if (preg_match('/SE 2.x/i', $user_agent, $regs)) {
        $browser = '(Internet Explorer ' . $version . ') 搜狗';
        $version = '';
    }
    if (preg_match('/FireFox\/([^\s]+)/i', $user_agent, $regs)) {
        $browser = 'FireFox';
        $version = $regs[1];
    }
    if (preg_match('/Lynx\/([^\s]+)/i', $user_agent, $regs)) {
        $browser = 'Lynx';
        $version = $regs[1];
    }
    if (preg_match('/Chrome\/([^\s]+)/i', $user_agent, $regs)) {
        $browser = 'Chrome';
        $version = $regs[1];

    }
    if ($browser != '') {
        return ['browser' => $browser, 'version' => $version];
    } else {
        return ['browser' => 'unknow browser', 'version' => 'unknow browser version'];
    }
}

/**
 * 判断一个数组中的其中一个值是否在数组中
 * @param array|string $needles
 * @param array $haystack
 * @return bool
 */
function easy_in_array(array|string $needles, array $haystack): bool
{
    if (!is_array($needles)) {
        return in_array($needles, $haystack);
    }
    foreach ($needles as $needle) {
        if (in_array($needle, $haystack)) {
            return true;
        }
    }
    return false;
}

/**
 * 计算库存辅助数量
 * @param $qty
 * @param $unit_convert
 * @param $unit_name
 * @param $big_unit_name
 * @return string
 */
function easy_unit_assist($qty, $unit_convert, $unit_name, $big_unit_name): string
{
    $unit_name = trim($unit_name);
    $unit_convert = trim($unit_convert);
    $big_unit_name = trim($big_unit_name);
    $qty = trim($qty);
    if (empty($big_unit_name) || !is_numeric($unit_convert) || $unit_convert <= 0 || !is_numeric($qty)) {
        return $qty . $unit_name;
    }
    $big = (int)floor($qty / $unit_convert);
    $small = $qty % $unit_convert;
    if ($big >= 0 && $small > 0) {
        return $big . $big_unit_name . '+' . $small . $unit_name;
    } elseif ($big > 0 && $small <= 0) {
        return $big . $big_unit_name;
    } else {
        return $small . $unit_name;
    }
}

/**
 * 检测发生变化的数据
 * @param array $old_data
 * @param array $new_data
 * @return array
 */
function easy_check_change_data(array $old_data = [], array $new_data = []): array
{
    $change_data = [];
    foreach ($old_data as $k => $item) {
        if (array_key_exists($k, $new_data) && $new_data[$k] != $item) {
            $change_data[$k] = ['old' => $item, 'new' => $new_data[$k]];
        }
    }
    return $change_data;
}

/**
 * 获取变化内容
 * @param array $change_data
 * @return string
 */
function easy_get_change_str(array $change_data): string
{
    $str = '';
    unset($change_data['operate_id'], $change_data['operate_by']);
    foreach ($change_data as $k => $v) {
        $str .= $k . ':' . $v['old'] . ' => ' . $v['new'];
    }
    return $str;
}

/**
 * 按照权重获取随机的key
 * @param array $data_list
 * @param string $weight_field
 * @param string $use_field
 * @return bool|string
 */
#[Pure] function easy_weight_with_use_num(array $data_list, string $weight_field = 'weight', string $use_field = 'use_num'): bool|string
{
    if (empty($data_list)) {
        return false;
    }
    $min_weight_use = null;
    $min_key = null;
    foreach ($data_list as $k => $item) {
        $use_num = $item[$use_field] ?? null;
        $weight = $item[$weight_field] ?? null;
        if (!is_numeric($weight) || $weight <= 0) {
            continue;
        }
        if (!is_numeric($use_num) || $use_num <= 0) {
            $use_num = 0;
        }
        $use_num = (float)$use_num;
        $weight = (float)$weight;
        $weight_use = round($use_num / $weight, 6);
        if (is_null($min_weight_use)) {
            $min_weight_use = $weight_use;
            $min_key = $k;
        } else {
            if (easy_float_gt($min_weight_use, $weight_use, 6)) {
                $min_weight_use = $weight_use;
                $min_key = $k;
            }
        }
    }
    if (is_null($min_key)) {
        return false;
    }
    return $min_key;
}

/**
 * 格式化数组
 * @param $data $key => $value
 * @return array
 */
function easy_format($data): array
{
    if (empty($data)) {
        return [];
    }
    $new_data = [];
    foreach ($data as $key => $item) {
        $new_data[] = [
            'id' => $key,
            'name' => $item
        ];
    }
    return $new_data;
}

/**
 * 格式化数组
 * @param $data $key => $value
 * @return array
 */
function easy_select_format($data): array
{
    if (empty($data)) {
        return [];
    }
    $new_data = [];
    foreach ($data as $key => $item) {
        $new_data[] = [
            'value' => $key,
            'title' => $item
        ];
    }
    return $new_data;
}

/**
 * 高亮显示文本差异
 * @param $str1
 * @param $str2
 * @return array|bool
 */
function diff_text($str1, $str2): bool|array
{
    if (empty($str1) || empty($str2)) {
        return false;
    }
    $arr1 = explode(' ', $str1);
    $arr2 = explode(' ', $str2);
    if (count($arr1) > count($arr2)) {
        for ($i = 0; $i < count($arr1) - count($arr2); $i++) {
            $arr2[] = '';
        }
    } else if (count($arr1) < count($arr2)) {
        for ($i = 0; $i < count($arr2) - count($arr1); $i++) {
            $arr1[] = '';
        }
    }
    foreach ($arr1 as $key => $val) {
        if ($val != $arr2[$key]) {
            $str1 = str_replace($val, '<span style="color:red;">' . $val . '</span>', $str1);
            $str2 = str_replace($arr2[$key], '<span style="color:red;">' . $arr2[$key] . '</span>', $str2);
        }
    }
    return [$str1, $str2];
}

/**
 * 格式化时间
 * @param int $time 分钟
 * @return int|string
 */
function easy_date_format(int $time): int|string
{
    if ($time < 1) {
        return 0;
    }
    $hours = floor($time / 60);
    $minutes = ($time % 60);
    if ($hours > 0) {
        return $hours . '小时' . $minutes . '分钟';
    }
    return $minutes . '分钟';
}

/**
 * 格式化时间显示
 * @param $time
 * @return string
 */
function showDate($time = NULL)
{
    $text = '';
    $time = $time === NULL || $time > time() ? time() : intval($time);
    $t = time() - $time; //时间差 （秒）
    $y = date('Y', $time) - date('Y', time());//是否跨年
    switch ($t) {
        case $t == 0:
            $text = '刚刚';
            break;
        case $t < 60:
            //$text = $t . '秒前'; // 一分钟内
            $text = '刚刚';
            break;
        case $t < 60 * 60:
            $text = floor($t / 60) . '分钟前'; //一小时内
            break;
        case $t < 60 * 60 * 24:
            $text = floor($t / (60 * 60)) . '小时前'; // 一天内
            break;
        case $t < 60 * 60 * 24 * 3:
            //$text = floor($time/(60*60*24)) ==1 ?'昨天 ' . date('H:i', $time) : '前天 ' . date('H:i', $time) ; //昨天和前天
            //break;
        case $t < 60 * 60 * 24 * 30:
            $text = sprintf('%d天前', floor($t / 86400));
            //$text = date('m月d日 H:i', $time); //一个月内
            break;
        case $t >= 60 * 60 * 24 * 30 && $t <= 60 * 60 * 24 * 30 * 12:
            $text = floor($t / (60 * 60 * 24 * 30)) . '个月前';
            break;
        case $t < 60 * 60 * 24 * 365 && $y == 0:
            $text = date('m月d日', $time); //一年内
            break;
        default:
            $text = date('Y年m月d日', $time); //一年以前
            break;
    }
    return $text;
}

/**
 * 秒数转时分秒
 */
function secondsToTime($seconds)
{
    $hours = floor($seconds / 3600); // 计算小时数
    $minutes = floor(($seconds / 60) % 60); // 计算分钟数
    $seconds = $seconds % 60; // 计算剩余的秒数

    // 格式化输出
    return sprintf('%02d时%02d分%02d秒', $hours, $minutes, $seconds);
}

/**
 * 数组转换为xml
 *
 * @param array $arr
 * @param int $level
 * @return string
 */
function arrayToXml(array $arr, int $level = 0): string
{
    $s = $level == 0 ? '<?xml version="1.0" encoding="UTF-8"?>' : '';
    foreach ($arr as $key => $value) {
        if (is_array($value)) {
            $s .= "<" . $key . ">" . arrayToXml($value, 1) . "</" . $key . ">";
        } else {
            $s .= "<" . $key . ">" . $value . "</" . $key . ">";
        }
    }
    return $s;
}

/**
 * 计算年龄
 * @param $birthday
 * @return int
 */
function getAge($birthday)
{
    if (empty($birthday)) {
        return '0岁';
    }
    if (!strtotime($birthday)) {
        return '0岁';
    }
    // 获取当前日期和时间
    $today = date('Y-m-d');
    // 获取生日日期
    // 计算年龄
    $age = date_diff(date_create($birthday), date_create($today))->y;
    return $age . '岁';
}
